
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

package com.hlkj.pay.interceptor;

import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.pojo.LocalContext;
import com.hlkj.framework.common.pojo.LocalRequest;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.common.util.web.WebFrameworkUtils;
import com.hlkj.pay.common.CommonResultCode;
import com.hlkj.pay.common.constants.AdminConstant;
import com.hlkj.pay.common.constants.MqConstant;
import com.hlkj.pay.config.ExtRocketMQTemplate;
import com.hlkj.pay.dto.LocalUserRequest;
import com.hlkj.pay.enums.AuthTokenEnum;
import com.hlkj.pay.infrastructure.model.user.UserRequestLogDO;
import com.hlkj.pay.util.TokenUtils;
import com.hlkj.pay.vo.common.resp.token.TokenInfoResp;

import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @author HlpayTeam
 * @data 2022/2/8 13:22
 */
@Component
@Slf4j
@Order(Integer.MIN_VALUE)
public class ClientTokenInterceptor implements HandlerInterceptor {

    @Value("${auth.signingKey}")
    private String signingKey;

    @Autowired
    private ExtRocketMQTemplate extRocketMQTemplate;

    private final MappingJackson2HttpMessageConverter errorHttpResponseConverter = new MappingJackson2HttpMessageConverter();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 这里是个坑，因为带请求带headers时，ajax会发送两次请求，
        // 第一次会发送OPTIONS请求，第二次才会发生get/post请求，所以要放行OPTIONS请求
        // 如果是OPTIONS请求，让其响应一个 200状态码，说明可以正常访问
        if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            // 放行OPTIONS请求
            return true;
        }
        // token 获取
        String header = WebFrameworkUtils.getRequest().getHeader(AdminConstant.AUTHORIZATION);
        if (StringUtil.isEmpty(header)) {
            header="";
            ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
            // this.errorHttpResponseConverter.write(CommonResult.error(CommonResultCode.TOKEN_PARAMETER_MISSING), MediaType.APPLICATION_JSON, httpResponse);
            log.warn("token is null:{}", header);
            // return false;
        }
        String token = header.replace(AdminConstant.BEARER_BANK, "");
        token = token.replace(AdminConstant.BEARER, "");
        log.info("clientTokenInterceptor:{}",token);
        // if (StringUtil.isEmpty(token)) {
        //     token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYmYiOjE3MzAwODQwNzEsInN5c1R5cGUiOiIyIiwiYXV0aFRva2VuVHlwZSI6IjEiLCJhcHBJZCI6Im1lcmNoYW50X2FwcF9pZCIsInNuIjoiIiwiZXhwIjoxNzMwMTcwNDcxLCJpYXQiOjE3MzAwODQwNzEsInVzZXJJZCI6IjUyIn0.cKHSCGe1VHl5tGze1aOuObJxaIoi3S_oV3cloddWkyc";
        // }
        if (StringUtil.isEmpty(token)) {
            ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
            this.errorHttpResponseConverter.write(CommonResult.error(CommonResultCode.TOKEN_PARAMETER_MISSING), MediaType.APPLICATION_JSON, httpResponse);
            log.warn("token is null:{}", token);
            return false;
        }
        boolean checkToken = TokenUtils.checkToken(token, signingKey);
        if (!checkToken) {
            ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
            this.errorHttpResponseConverter.write(CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR), MediaType.APPLICATION_JSON, httpResponse);
            log.warn("checkToken fail token:{}", token);
            return false;
        }
        TokenInfoResp accessTokenInfo = TokenUtils.parseToken(token);
        if (log.isDebugEnabled()) {
            log.debug("TokenInfo:{}", JsonUtils.toJsonString(accessTokenInfo));
        }
        // token 失效
        Long expireTime = accessTokenInfo.getExpireTime();
        long currentTimeMillis = System.currentTimeMillis();
        long now = currentTimeMillis / 1000;
        if (expireTime == null || now > expireTime) {
            // ServletServerHttpResponse httpResponse = new
            // ServletServerHttpResponse(response);
            // this.errorHttpResponseConverter.write(CommonResult.error(CommonResultCode.TOKEN_PARAMETER_EXPIRE),
            // MediaType.APPLICATION_JSON,
            // httpResponse);
            log.warn("token is expire now:{},expireTime:{}", now, expireTime);
            // return false;
        }
        AuthTokenEnum.AUTH_TOKEN_TYPE authTokenType = AuthTokenEnum.AUTH_TOKEN_TYPE.from(accessTokenInfo.getAuthTokenType());
        switch (authTokenType) {
            case REFRESH_TOKEN:
                ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
                this.errorHttpResponseConverter.write(CommonResult.error(CommonResultCode.TOKEN_PARAMETER_ERROR), MediaType.APPLICATION_JSON, httpResponse);
                log.warn("token is not accessToken accessTokenInfo:{}", JsonUtils.toJsonString(accessTokenInfo));
                return false;
        }
        LocalRequest localRequest = LocalContext.get();
        if (localRequest == null) {
            localRequest = new LocalUserRequest();
        }
        LocalUserRequest localUserRequest = (LocalUserRequest) localRequest;
        // LocalAdminUserRequest localUserRequest = new LocalAdminUserRequest();
        localUserRequest.setUserId(accessTokenInfo.getUserId());
        localUserRequest.setRequestMethod(request.getMethod());
        localUserRequest.setAccessToken(token);
        localUserRequest.setLogCode(UUID.randomUUID().toString());
        localUserRequest.setSysType(accessTokenInfo.getSysType());
        localUserRequest.setSn(accessTokenInfo.getSn());
        localUserRequest.setStartTime(currentTimeMillis);
        String servletPath = request.getServletPath();
        if (StringUtils.isBlank(servletPath)) {
            servletPath = request.getPathInfo();
        }
        localUserRequest.setRequestUrl(servletPath);
        localUserRequest.setRequestMethod(request.getMethod());
        LocalContext.set(localUserRequest);
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
        LocalUserRequest localUserRequest = (LocalUserRequest) LocalContext.get();
        UserRequestLogDO userRequestLogDO = new UserRequestLogDO();
        userRequestLogDO.setUserId(localUserRequest.getUserId());
        userRequestLogDO.setLogCode(localUserRequest.getLogCode());
        userRequestLogDO.setParams(localUserRequest.getParams());
        userRequestLogDO.setResponse(localUserRequest.getResponse());
        userRequestLogDO.setIp(localUserRequest.getIp());
        userRequestLogDO.setIpLocation(localUserRequest.getIpLocation());
        userRequestLogDO.setRequestUrl(localUserRequest.getRequestMethod()+" "+localUserRequest.getRequestUrl());
        userRequestLogDO.setCostTime(System.currentTimeMillis() - localUserRequest.getStartTime());
        userRequestLogDO.setCreateTime(System.currentTimeMillis());
        try {
            extRocketMQTemplate.send(MqConstant.CLIENT_REQUEST_LOG_TOPIC, userRequestLogDO);
        }
        catch (Exception e) {
            log.error("请求参数记录失败:{}", e.getMessage());
        }
        LocalContext.destroy();
    }
}
